package com.xjgzinfo.usdp.config;

import cn.hutool.core.util.ArrayUtil;
import com.xjgzinfo.usdp.constant.AuthConstant;
import com.xjgzinfo.usdp.security.oauth2.JwtGrantedAuthorityConverter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.*;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Import(SecurityProblemSupport.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final ApplicationProperties applicationProperties;
    private final IgnoreUrlsConfig ignoreUrlsConfig;
    private final SecurityProblemSupport problemSupport;
    @Value("${spring.security.oauth2.client.provider.oidc.issuer-uri:}")
    private String issuerUri;
    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri:}")
    private String jwt_issuerUri;
    @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri:}")
    private String jwt_jwkSetUri;

    public SecurityConfiguration(ApplicationProperties applicationProperties, IgnoreUrlsConfig ignoreUrlsConfig, SecurityProblemSupport problemSupport) {
        this.problemSupport = problemSupport;
        this.ignoreUrlsConfig = ignoreUrlsConfig;
        this.applicationProperties = applicationProperties;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.cors()
                .and()
                .csrf()
                .disable()
                .exceptionHandling()
                .authenticationEntryPoint(problemSupport)
                .accessDeniedHandler(problemSupport)
                .and()
                .headers()
                .contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:").and().referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
                .and()
                .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
                .and()
                .featurePolicy("geolocation 'none'; midi 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; fullscreen 'self'; payment 'none'")
                .and()
                .frameOptions()
                .deny()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(ArrayUtil.toArray(ignoreUrlsConfig.getUrls(), String.class)).permitAll()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
//                .antMatchers("/api/**").authenticated()
                .antMatchers("/actuator/**").hasAuthority(AuthConstant.ADMIN)
                .and()
                .oauth2ResourceServer()
                .jwt()
                .jwtAuthenticationConverter(authenticationConverter())
                .and()
                .and().oauth2Client();
        // @formatter:on
    }

    Converter<Jwt, AbstractAuthenticationToken> authenticationConverter() {
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(new JwtGrantedAuthorityConverter());
        return jwtAuthenticationConverter;
    }

    @Bean
    JwtDecoder jwtDecoder() {
        NimbusJwtDecoder jwtDecoder = null;
        OAuth2TokenValidator<Jwt> withIssuer = null;
        if (!StringUtils.isEmpty(issuerUri)) {
            jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(issuerUri);
            withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
        }
        if (jwtDecoder == null && !StringUtils.isEmpty(jwt_issuerUri)) {
            jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromIssuerLocation(jwt_issuerUri);
            withIssuer = JwtValidators.createDefault();
        }
        if (jwtDecoder == null && !StringUtils.isEmpty(jwt_jwkSetUri)) {
            jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwt_jwkSetUri).build();
            withIssuer = JwtValidators.createDefault();
        }
        if (jwtDecoder != null) {
            //OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(applicationProperties.getSecurity().getOauth2().getAudience());
            //audienceValidator
            OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer);
            jwtDecoder.setJwtValidator(withAudience);
        }
        return jwtDecoder;
    }


}
